package com.framework.seedkit.device.ble;

import android.Manifest;
import android.annotation.TargetApi;
import android.app.ProgressDialog;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCharacteristic;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ListView;
import android.widget.Toast;

import com.clj.fastble.BleManager;
import com.clj.fastble.bluetooth.BleGattCallback;
import com.clj.fastble.conn.BleCharacterCallback;
import com.clj.fastble.exception.BleException;
import com.clj.fastble.scan.ListScanCallback;
import com.clj.fastble.utils.BluetoothUtil;
import com.clj.fastble.utils.HexUtil;
import com.framework.seedkit.R;
import com.framework.seedkit.base.BaseActivity;
import com.framework.seedkit.utils.LOG;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import butterknife.BindView;

import static android.widget.Toast.LENGTH_LONG;
import static android.widget.Toast.LENGTH_SHORT;

public class BleDeviceAct extends BaseActivity implements View.OnClickListener {
    @BindView(R.id.scan_btn) Button scan_btn;
    @BindView(R.id.scan_connect_btn) Button scan_connect_btn;
    @BindView(R.id.scan_specify_connect_btn) Button scan_specify_connect_btn;
    @BindView(R.id.notify_btn) Button notify_btn;
    @BindView(R.id.indicate_btn) Button indicate_btn;
    @BindView(R.id.write_btn) Button write_btn;
    @BindView(R.id.close_btn) Button close_btn;
    @BindView(R.id.refresh_btn) Button refresh_btn;

    private static final String UUID_CLIENT_CHARACTERISTIC_CONFIG_DESCRIPTOR = "00002902-0000-1000-8000-00805f9b34fb";
    public static final int REQUEST_CODE_ACCESS_COARSE_LOCATION = 1;

    // 下面的所有UUID及指令请根据实际设备替换
    private static final String UUID_SERVICE_LISTEN = "00001810-0000-1000-8000-00805f9b34fb";       // 下面两个特征值所对应的service的UUID
    private static final String UUID_LISTEN_INDICATE = "00002A35-0000-1000-8000-00805f9b34fb";      // indicate特征值的UUID
    private static final String UUID_LISTEN_NOTIFY = "00002A36-0000-1000-8000-00805f9b34fb";        // notify特征值的UUID

    private static final String UUID_SERVICE_OPERATE = "0000fff0-0000-1000-8000-00805f9b34fb";      // 下面两个特征值所对应的service的UUID
    private static final String UUID_OPERATE_WRITE = "0000fff1-0000-1000-8000-00805f9b34fb";        // 设备写特征值的UUID
    private static final String UUID_OPERATE_NOTIFY = "0000fff2-0000-1000-8000-00805f9b34fb";       // 设备监听写完之后特征值数据改变的UUID

    private static final String SAMPLE_WRITE_DATA = "55aa0bb2100705100600ee";     // 要写入设备某一个特征值的指令

    private static final long TIME_OUT = 10000;                                   // 扫描超时时间
    private static final String DEVICE_NAME = "这里写你的设备名";                   // 符合连接规则的蓝牙设备名，即：device.getName
    private static final String TAG = "ble_sample";
    private BleManager bleManager;                                             // Ble核心管理类

    private ArrayList<BluetoothDevice> bluetoothDevices = new ArrayList<BluetoothDevice>();

    private CommonAdapter<Map<String, Object>> deviceAdapter;
    private List<Map<String, Object>> deviceList;
    private ListView listView_device;
    private ProgressDialog progressDialog;
    private UIHandler m_UIhandler;

    private int currConnectDeviceIndex = 0;  //保存连接设备的索引
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        m_UIhandler = new UIHandler();
        bleManager = BleManager.getInstance();
        bleManager.init(this);

        deviceList = new ArrayList<>();
        deviceAdapter = new CommonAdapter<Map<String, Object>>(
                this, R.layout.item_device, deviceList) {
            @Override
            public void convert(final ViewHolder holder, final Map<String, Object> deviceMap) {
                if (holder == null || deviceMap == null || !deviceMap.containsKey("name")
                        || !deviceMap.containsKey("address")
                        || !deviceMap.containsKey("isConnect")) {
                    return;
                }

                holder.setText(R.id.txtv_name, (String)deviceMap.get("name"));
                holder.setText(R.id.txtv_address, (String)deviceMap.get("address"));
                holder.setText(R.id.txtv_connState, ((boolean) deviceMap.get("isConnect")) ?
                        getResources().getString(R.string.state_connected) :
                        getResources().getString(R.string.state_disconnected));
                holder.setText(R.id.btn_connect, ((boolean) deviceMap.get("isConnect")) ?
                        getResources().getString(R.string.disconnected) :
                        getResources().getString(R.string.connected));
                holder.getView(R.id.btn_connect).setOnClickListener(new View.OnClickListener()
                {
                    @TargetApi(18)
                    @Override
                    public void onClick(View v)
                    {
                        if ((boolean) deviceMap.get("isConnect")) {
                            bleManager.disconnectDevice();
                            deviceList.get(holder.getPosition()).put("isConnect", false);
                            sendMsgToUI(Constant.HANDLER_MSG_DEV_DISCONNECT, R.string.disconnect_successed);
                        } else {
                            showLoadingDialog(getString(R.string.connecting));
                            currConnectDeviceIndex = holder.getPosition();
                            bleManager.connectDevice(bluetoothDevices.get(holder.getPosition()), gattCallback);
                            showLoadingDialog(getString(R.string.connecting));
                        }
                    }
                });
            }
        };

        listView_device.setAdapter(deviceAdapter);

        verifyIfRequestPermission();
    }

    @Override
    protected int getLayoutId() {
        return R.layout.act_ble_layout;
    }

    @Override
    public void initView() {
        scan_btn.setOnClickListener(this);
        scan_connect_btn.setOnClickListener(this);
        scan_specify_connect_btn.setOnClickListener(this);
        notify_btn.setOnClickListener(this);
        indicate_btn.setOnClickListener(this);
        write_btn.setOnClickListener(this);
        close_btn.setOnClickListener(this);
        refresh_btn.setOnClickListener(this);

        listView_device = (ListView) findViewById(R.id.listView_device);
    }

    @TargetApi(18)
    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.scan_btn:
                showLoadingDialog(getString(R.string.scanning));
                if (deviceList != null || deviceList.size() > 0) {
                    deviceList.clear();
                }
                bleManager.scanDevice(new ListScanCallback(TIME_OUT) {
                    @Override
                    public void onDeviceFound(BluetoothDevice[] devices) {
                        Log.i(TAG, "共发现" + devices.length + "台设备");
                        for (int i = 0; i < devices.length; i++) {
                            Log.i(TAG, "name:" + devices[i].getName() + "------mac:" + devices[i].getAddress());
                            HashMap<String, Object> deviceMap = new HashMap<>();
                            deviceMap.put("name", devices[i].getName());
                            deviceMap.put("address", devices[i].getAddress());
                            deviceMap.put("isConnect", false);
                            if (deviceList != null) {
                                deviceList.clear();
                            }
                            deviceList.add(deviceMap);
                            bluetoothDevices.add(devices[i]);
                        }

                        deviceAdapter.notifyDataSetChanged();
                    }

                    @Override
                    public void onScanTimeout() {
                        super.onScanTimeout();
                        dismissLoadingDialog();
                        Log.i(TAG, "搜索结束,为找到蓝牙设备");
                        Toast.makeText(BleDeviceAct.this, "未找到蓝牙设备", LENGTH_LONG);
                    }
                });
                break;
            case R.id.scan_connect_btn:
                showLoadingDialog(getString(R.string.scanning));
                if (bluetoothDevices == null || bluetoothDevices.size() < 1)
                    return;
                BluetoothDevice sampleDevice = bluetoothDevices.get(0);

                bleManager.connectDevice(sampleDevice, new BleGattCallback() {
                    @TargetApi(18)
                    @Override
                    public void onConnectSuccess(BluetoothGatt gatt, int status) {
                        Log.i(TAG, "连接成功！");
                        gatt.discoverServices();                // 连接上设备后搜索服务
                    }

                    @Override
                    public void onServicesDiscovered(BluetoothGatt gatt, int status) {
                        Log.i(TAG, "服务被发现！");
                        BluetoothUtil.printServices(gatt);            // 打印该设备所有服务、特征值
                        bleManager.getBluetoothState();               // 打印与该设备的当前状态
                    }

                    @Override
                    public void onConnectFailure(BleException exception) {
                        Log.i(TAG, "连接失败或连接中断：" + '\n' + exception.toString());
                        bleManager.handleException(exception);
                    }
                });
                break;
            case R.id.scan_specify_connect_btn:
                showLoadingDialog(getString(R.string.scanning));
                bleManager.connectDevice(
                        DEVICE_NAME,
                        TIME_OUT,
                        new BleGattCallback() {
                            @Override
                            public void onConnectSuccess(BluetoothGatt gatt, int status) {
                                Log.i(TAG, "连接成功！");
                                gatt.discoverServices();                // 连接上设备后搜索服务
                            }

                            @Override
                            public void onServicesDiscovered(BluetoothGatt gatt, int status) {
                                Log.i(TAG, "服务被发现！");
                                BluetoothUtil.printServices(gatt);            // 打印该设备所有服务、特征值
                                bleManager.getBluetoothState();               // 打印与该设备的当前状态
                            }

                            @Override
                            public void onConnectFailure(BleException exception) {
                                Log.i(TAG, "连接失败或连接中断：" + '\n' + exception.toString());
                                bleManager.handleException(exception);
                            }
                        });
                break;
            case R.id.indicate_btn:
                bleManager.indicateDevice(
                        UUID_SERVICE_LISTEN,
                        UUID_LISTEN_INDICATE,
                        UUID_CLIENT_CHARACTERISTIC_CONFIG_DESCRIPTOR,
                        new BleCharacterCallback() {
                            @Override
                            public void onSuccess(BluetoothGattCharacteristic characteristic) {
                                Log.d(TAG, "特征值Indication通知数据回调： "
                                        + '\n' + Arrays.toString(characteristic.getValue())
                                        + '\n' + HexUtil.encodeHexStr(characteristic.getValue()));
                            }

                            @Override
                            public void onFailure(BleException exception) {
                                Log.e(TAG, "特征值Indication通知回调失败: " + '\n' + exception.toString());
                                bleManager.handleException(exception);
                            }
                        });
                break;
            case R.id.notify_btn:
                bleManager.notifyDevice(
                        UUID_SERVICE_LISTEN,
                        UUID_LISTEN_NOTIFY,
                        UUID_CLIENT_CHARACTERISTIC_CONFIG_DESCRIPTOR,
                        new BleCharacterCallback() {
                            @Override
                            public void onSuccess(BluetoothGattCharacteristic characteristic) {
                                Log.d(TAG, "特征值Notification通知数据回调： "
                                        + '\n' + Arrays.toString(characteristic.getValue())
                                        + '\n' + HexUtil.encodeHexStr(characteristic.getValue()));
                            }

                            @Override
                            public void onFailure(BleException exception) {
                                Log.e(TAG, "特征值Notification通知回调失败: " + '\n' + exception.toString());
                                bleManager.handleException(exception);
                            }
                        });
                break;
            case R.id.write_btn:
                        bleManager.writeDevice(
                        UUID_SERVICE_OPERATE,
                        UUID_OPERATE_WRITE,
                        UUID_CLIENT_CHARACTERISTIC_CONFIG_DESCRIPTOR,
                        HexUtil.hexStringToBytes(SAMPLE_WRITE_DATA),
                        new BleCharacterCallback() {
                            @Override
                            public void onSuccess(BluetoothGattCharacteristic characteristic) {
                                Log.d(TAG, "写特征值成功: "
                                        + '\n' + Arrays.toString(characteristic.getValue())
                                        + '\n' + HexUtil.encodeHexStr(characteristic.getValue()));
                            }

                            @Override
                            public void onFailure(BleException exception) {
                                Log.e(TAG, "写读特征值失败: " + '\n' + exception.toString());
                                bleManager.handleException(exception);
                            }
                        });
                break;
            case R.id.refresh_btn:
                bleManager.refreshDeviceCache();
                break;
            case R.id.close_btn:
                bleManager.closeBluetoothGatt();
                break;
        }
    }

    //权限请求后的回调
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults)
    {
        if (requestCode == REQUEST_CODE_ACCESS_COARSE_LOCATION) {
            LOG.i("onRequestPermissionsResult: permissions.length = " + permissions.length +
                    ", grantResults.length = " + grantResults.length);
            if (grantResults.length > 0
                    && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                // permission was granted, yay! Do the
                // contacts-related task you need to do.
            } else {
                // permission denied, boo! Disable the
                // functionality that depends on this permission.
                Toast.makeText(this, "位置访问权限被拒绝将无法搜索到ble设备", Toast.LENGTH_SHORT).show();
            }
        } else {
            super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        }
    }


    private BleGattCallback gattCallback =  new BleGattCallback() {
        @TargetApi(18)
        @Override
        public void onConnectSuccess(BluetoothGatt gatt, int status) {
            Log.i(TAG, getString(R.string.connect_successed));
            deviceList.get(currConnectDeviceIndex).put("isConnect", true);
            sendMsgToUI(Constant.HANDLER_MSG_DEV_CONNECT, R.string.connect_successed);
            gatt.discoverServices();              // 连接上设备后搜索服务
        }

        @Override
        public void onServicesDiscovered(BluetoothGatt gatt, int status) {
            Log.i(TAG, "服务被发现！");
            BluetoothUtil.printServices(gatt);            // 打印该设备所有服务、特征值
            bleManager.getBluetoothState();               // 打印与该设备的当前状态
        }

        @Override
        public void onConnectFailure(BleException exception) {
            Log.i(TAG, "连接失败或连接中断：" + '\n' + exception.toString());
            Toast.makeText(BleDeviceAct.this, getString(R.string.connect_failed), LENGTH_SHORT);
            dismissLoadingDialog();
            bleManager.handleException(exception);
        }
    };

    /*******************************移除某一个回调示例**********************************/

    /**
     * 将回调实例化，而不是以匿名对象的形式
     */
    BleCharacterCallback bleCharacterCallback = new BleCharacterCallback() {
        @TargetApi(18)
        @Override
        public void onSuccess(BluetoothGattCharacteristic characteristic) {
            Log.d(TAG, "特征值Notification通知数据回调： "
                    + '\n' + Arrays.toString(characteristic.getValue())
                    + '\n' + HexUtil.encodeHexStr(characteristic.getValue()));
        }

        @Override
        public void onFailure(BleException exception) {
            Log.e(TAG, "特征值Notification通知回调失败: " + '\n' + exception.toString());
            bleManager.handleException(exception);
        }
    };

    private void addAndRemove() {

        /**需要使用的时候，作为参数传入*/
        bleManager.notifyDevice(
                UUID_SERVICE_OPERATE,
                UUID_OPERATE_NOTIFY,
                UUID_CLIENT_CHARACTERISTIC_CONFIG_DESCRIPTOR,
                bleCharacterCallback);

        /**不需要再监听特征值变化的时候，将该回调接口对象移除*/
        bleManager.removeBleCharacterCallback(bleCharacterCallback);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        bleManager.closeBluetoothGatt();
        bleManager.disableBluetooth();
    }

    public void showLoadingDialog(String message)
    {
        if (progressDialog == null) {
            progressDialog = new ProgressDialog(this);
        }

        progressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
        progressDialog.setCanceledOnTouchOutside(false);
        progressDialog.setMessage(message);
        progressDialog.show();
    }

    public void dismissLoadingDialog()
    {
        if (progressDialog == null) return;
        progressDialog.dismiss();
        progressDialog = null;
    }

    public void sendMsgToUI(int type, int stringId) {
        String msgKey = "strMsg";

        Message msg = new Message();
        Bundle bundle = new Bundle();
        msg.what = type;
        bundle.putString(msgKey, getString(stringId));
        msg.setData(bundle);
        BleDeviceAct.this.m_UIhandler.sendMessage(msg);
    }

    private class UIHandler extends Handler
    {
        @Override
        public void handleMessage(Message msg)
        {
            super.handleMessage(msg);
            Bundle bundle = msg.getData();
            String strMsg = bundle.getString("strMsg");
            switch (msg.what)
            {
                case Constant.HANDLER_MSG_DEV_CONNECT:
                    deviceAdapter.notifyDataSetChanged();
                    dismissLoadingDialog();
//                    appendLogView(strMsg);
                    Toast.makeText(BleDeviceAct.this, strMsg, Toast.LENGTH_SHORT);
                    dismissLoadingDialog();
                    break;
                case Constant.HANDLER_MSG_DEV_DISCONNECT:
                    deviceAdapter.notifyDataSetChanged();
                    Toast.makeText(BleDeviceAct.this, strMsg, Toast.LENGTH_SHORT);
                    break;
                default:
                    Log.e(TAG, "Handler GUI message not support...");
            }

        }
    }

    //Android 6.0权限请求
    private void verifyIfRequestPermission()
    {
        if (Build.VERSION.SDK_INT >= 23) {
            LOG.i("onCreate: checkSelfPermission");
            if (ContextCompat.checkSelfPermission(this,
                    Manifest.permission.ACCESS_COARSE_LOCATION)
                    != PackageManager.PERMISSION_GRANTED) {
                LOG.i("onCreate: Android 6.0 动态申请权限");

                if (ActivityCompat.shouldShowRequestPermissionRationale(this,
                        Manifest.permission.READ_CONTACTS)) {
                    LOG.i("*********onCreate: shouldShowRequestPermissionRationale**********");
                    Toast.makeText(this, "只有允许访问位置才能搜索到蓝牙设备", Toast.LENGTH_SHORT).show();
                } else {
                    ActivityCompat.requestPermissions(this,
                            new String[]{Manifest.permission.ACCESS_COARSE_LOCATION,
                                    Manifest.permission.ACCESS_FINE_LOCATION},
                            REQUEST_CODE_ACCESS_COARSE_LOCATION);
                }
            } else {
            }
        }
    }
}
